package org.realogic.cfm.persistence.query;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.realogic.cfm.util.UtilString;

public final class ConditionStatementParser {

	private ConditionStatementParser() {

	}

	public static QueryCondition createQueryCondition(String theQryLogicRule, Map<String, String> theParamName_valueMapping, Map<String, QueryFieldDef> theParamName_QueryFieldMapping) {
		// 如果条件没有分组的情况
		if (theQryLogicRule.indexOf("(") == -1) {
			return createSimpleQueryCondition(theQryLogicRule, theParamName_valueMapping, theParamName_QueryFieldMapping);
		}

		// 获取SQL-Rule
		String tokens[] = filerQryLogicRule(theQryLogicRule, theParamName_valueMapping);
		if (tokens == null) {
			return null;
		}
		// 创建条件对象
		QueryCondition condition = new QueryCondition();
		// 参数SQL 语句
		StringBuffer buf = new StringBuffer(tokens.length * 20);
		for (int i = 0; i < tokens.length; i++) {
			String token = tokens[i];
			if (token.equals("and")) {
				buf.append(" and ");
			} else if (token.equals("or")) {
				buf.append(" or ");
			} else if (token.equals(")") || token.equals("(")) {
				buf.append(token);
			} else {
				QueryFieldDef tmpQueryFieldDef = theParamName_QueryFieldMapping.get(token);
				buf.append(tmpQueryFieldDef.getSQLStatement());
				String tmpValue = theParamName_valueMapping.get(token);
				condition.addParameter(tmpQueryFieldDef.getParameter(tmpValue));
			}
		}
		condition.addSqlWhereStatement(buf.toString());
		return condition;
	}

	// 创建简单的查询,比如："f1 and f2 and f3 and f4"
	private static QueryCondition createSimpleQueryCondition(String theQryLogicRule, Map<String, String> theParamName_valueMapping, Map<String, QueryFieldDef> theParamName_QueryFieldMapping) {
		// 获取SQL-Rule
		String tokens[] = filerSimpleQryLogicRule(theQryLogicRule, theParamName_valueMapping);
		if (tokens == null) {
			return null;
		}
		// 创建条件对象
		QueryCondition condition = new QueryCondition();
		// 参数SQL 语句
		StringBuffer buf = new StringBuffer(tokens.length * 20);
		String token = null;
		for (int i = 0; i < tokens.length; i++) {
			token = tokens[i];
			if (i == 0) {
				if (token.equals("and") || token.equals("or")) {
					continue;
				}
			}
			if (token.equals("and")) {
				buf.append(" and ");
			} else if (token.equals("or")) {
				buf.append(" or ");
			} else {
				QueryFieldDef tmpQueryFieldDef = theParamName_QueryFieldMapping.get(token);
				buf.append(tmpQueryFieldDef.getSQLStatement());
				String tmpValue = theParamName_valueMapping.get(token);
				condition.addParameter(tmpQueryFieldDef.getParameter(tmpValue));
			}
		}
		condition.addSqlWhereStatement(buf.toString());
		return condition;
	}

	private static String[] filerQryLogicRule(String theQryLogicRule, Map<String, String> theParamName_valueMapping) {
		String tokens[] = getTokens(theQryLogicRule);
		for (int i = 0; i < tokens.length; i++) {
			String token = tokens[i];
			if (!token.equals("and") && !token.equals("or") && !token.equals(")") && !token.equals("(")) {
				String value = theParamName_valueMapping.get(token);
				if (value == null || value.length() == 0) {
					tokens[i] = "@";
				}
			}
		}
		Stack<INode> stack = new Stack<INode>();
		Stack<NodeGroup> stackGroup = new Stack<NodeGroup>();
		INode node = null;
		for (int i = tokens.length - 1; i >= 0; i--) {
			String token = tokens[i];
			if (token.equals("and")) {
				if (node != null && !node.isEmpty()) {
					node.setPrepend("and");
				}
			} else if (token.equals("or")) {
				if (node != null && !node.isEmpty()) {
					node.setPrepend("or");
				}
			} else if (token.equals("(")) {
				node = (NodeGroup) stackGroup.pop();
				if (stackGroup.size() > 0) {
					if (node != null) {
						NodeGroup tmpGroup = (NodeGroup) stackGroup.get(stackGroup.size() - 1);
						if (node != null && !node.isEmpty()) {
							tmpGroup.addFirst(node);
						}
					}
				} else {
					if (node != null && !node.isEmpty()) {
						stack.add(node);
					}
				}
			} else if (token.equals(")")) {
				NodeGroup tmpGroup = new NodeGroup();
				stackGroup.add(tmpGroup);
			} else if (token.equals("@")) {
				node = null;
			} else {
				node = new Node(token);
				if (stackGroup.size() == 0) {
					stack.add(node);
				} else {
					NodeGroup tmpGroup = (NodeGroup) stackGroup.get(stackGroup.size() - 1);
					tmpGroup.addFirst(node);
				}
			}
		}
		String newTokens[] = null;
		int size = stack.size();
		if (size > 0) {
			INode tmpNode = (INode) stack.get(size - 1);
			if (tmpNode.getPrepend().length() > 0) {
				tmpNode.setPrepend("");
			}
			StringBuffer buf = new StringBuffer(size * 10);
			for (int i = size - 1; i >= 0; i--) {
				INode tmp = (INode) stack.get(i);
				buf.append(tmp.getContent());
			}
			// 创建new tokens
			newTokens = getTokens(buf.toString());
		}
		// 清除
		stackGroup.clear();
		stack.clear();
		return newTokens;
	}

	private static String[] filerSimpleQryLogicRule(String theQryLogicRule, Map<String, String> theParamName_valueMapping) {
		String tokens[] = getTokens(theQryLogicRule);
		int emptyCount = 0;
		for (int i = 0; i < tokens.length; i++) {
			String token = tokens[i];
			if (!token.equals("and") && !token.equals("or") && !token.equals(")") && !token.equals("(")) {
				String value = theParamName_valueMapping.get(token);
				if (value == null || value.length() == 0) {
					tokens[i] = "";
					emptyCount++;
					if ((i - 1) > 0) {
						tokens[i - 1] = "";
						emptyCount++;
					}
				}
			}
		}
		if (emptyCount == tokens.length) {
			return null;
		}
		if (emptyCount == 0) {
			return tokens;
		}
		String newTokens[] = new String[tokens.length - emptyCount];
		int k = 0;
		for (int i = 0; i < tokens.length; i++) {
			String token = tokens[i];
			if (token.length() > 0) {
				newTokens[k] = tokens[i];
				k++;
			}

		}
		return newTokens;
	}

	protected static String[] getTokens(String s) {
		List<String> parsedTokens = new ArrayList<String>();
		int start = 0;
		String tmp = null;
		for (int i = 0; i < s.length(); i++) {
			if (s.charAt(i) == '(') {
				parsedTokens.add("(");
				start = i + 1;
			} else if (s.charAt(i) == ')') {
				if (start < i) {
					tmp = s.substring(start, i).trim();
					if (tmp.length() > 0) {
						parsedTokens.add(tmp);
					}
				}
				parsedTokens.add(")");
				start = i + 1;
			} else if (s.charAt(i) == ' ') {
				tmp = s.substring(start, i).trim();
				if (tmp.length() > 0) {
					if (tmp.equalsIgnoreCase("and")) {
						parsedTokens.add("and");
					} else if (tmp.equalsIgnoreCase("or")) {
						parsedTokens.add("or");
					} else {
						parsedTokens.add(tmp);
					}
				}
				start = i + 1;
			}
		}
		if (start < s.length()) {
			tmp = s.substring(start).trim();
			if (tmp.length() > 0) {
				parsedTokens.add(tmp);
			}
		}

		return parsedTokens.toArray(new String[parsedTokens.size()]);
	}

	protected static String[] getTokens_(String qryLogicRule) {
		String s1 = UtilString.replace(qryLogicRule, " and ", "|and|", true);
		String s2 = UtilString.replace(s1, " or ", "|or|", true);
		String s3 = UtilString.replace(s2, "(", "(|", true);
		String s4 = UtilString.replace(s3, ")", "|)", true);
		String tmps[] = UtilString.tokenize(s4, "|");
		List<String> tmpList = new ArrayList<String>(tmps.length);
		for (int i = 0; i < tmps.length; i++) {
			String token = tmps[i].trim();
			if (token.length() != 0) {
				tmpList.add(token);
			}
		}
		String results[] = new String[tmpList.size()];
		for (int i = 0; i < results.length; i++) {
			results[i] = tmpList.get(i);
			// System.out.print(results[i]+"***");
		}
		// System.out.println("=================================");
		tmpList.clear();
		return results;
	}

	private static interface INode {
		public String getPrepend();

		public void setPrepend(String prepend);

		public String getContent();

		public boolean isEmpty();
	}

	private static class Node implements INode {
		private String prepend = "";

		private String unit;

		public Node(String unit) {
			this.unit = unit;
		}

		public String getContent() {
			if (prepend.length() > 0) {
				return " " + prepend + " " + unit;
			}
			return unit;
		}

		public boolean isEmpty() {
			return false;
		}

		public String getPrepend() {
			if (prepend == null) {
				return "";
			}
			return prepend.trim();
		}

		public void setPrepend(String prepend) {
			this.prepend = prepend;
		}
	}

	private static class NodeGroup implements INode {
		private String prepend = "";

		private List<INode> list = new LinkedList<INode>();

		public void addFirst(INode s) {
			list.add(0, s);
		}

		public String getPrepend() {
			if (prepend == null) {
				return "";
			}
			return prepend.trim();
		}

		public void setPrepend(String prepend) {
			this.prepend = prepend;
		}

		public boolean isEmpty() {
			return list.size() == 0;
		}

		public String getContent() {
			if (isEmpty()) {
				return "";
			}
			int size = list.size();
			StringBuffer buf = new StringBuffer(size * 8);
			if (prepend.length() > 0) {
				buf.append(" ");
				buf.append(prepend);
				buf.append(" ");
			}
			buf.append("( ");
			INode tmpNode = (INode) list.get(0);
			if (tmpNode.getPrepend().length() > 0) {
				tmpNode.setPrepend("");
			}
			for (int i = 0; i < size; i++) {
				tmpNode = (INode) list.get(i);
				buf.append(tmpNode.getContent());
				buf.append(" ");
			}
			buf.append(")");
			return buf.toString();
		}
	}
}